/*
 * @Description: In User Settings Edit
 * @Author: your name
 * @Date: 2019-09-16 14:25:06
 * @LastEditTime: 2019-12-15 21:32:54
 * @LastEditors: select last editor
 */



#include "pb.h"

#include "crc16.h"

static uint16_t transaction_id = 1; 

bool pb_check_packet_integrity( pb_msg_t * p_msg)
{
	if( p_msg == NULL || p_msg->p_data == NULL )
		return false; 
	
	if( p_msg->crc != crc16_compute( p_msg->p_data, p_msg->len, 0 ) )
		return false; 
	
	return true; 
}

bool pb_check_flag_has_crypto( pb_msg_t * p_msg)
{
	return (p_msg->flag & FLAG_CRYPTO_MASK) > 0 ;
}


pb_stream_t pb_stream_from_buffer( uint8_t* data, uint32_t size)
{
	return (pb_stream_t){ .p_data = data, .size = size}; 
}


int pb_assemble_rx_msg( pb_msg_t * p_msg, const uint8_t* data_in, uint16_t size )
{
	if( p_msg == NULL || data_in == NULL )
		return PB_FSM_IDLE; 
	
	switch( p_msg->state )
	{
		case PB_FSM_IDLE:{ // check header 
			if( size < PB_HEADE_LENGTH )
			break;
			
			pb_msg_t * p_temp_msg = (pb_msg_t*)data_in;
			if( p_temp_msg->magic != PB_MAGIC_CODE)
				break;
			
			if( p_temp_msg->len > p_msg->max_len_support || p_temp_msg->len == 0 )
				break;
			
			memcpy( (uint8_t*)p_msg, data_in, 8 );
			uint16_t bytes_remain = size - 8;
			if( bytes_remain >= p_msg->len ) {
				bytes_remain = p_msg->len;			
			}
			memcpy( p_msg->p_data, data_in + 8, bytes_remain );
			p_msg->bytes_received = bytes_remain; 
			if( p_msg->bytes_received >= p_msg->len ){
				if( p_msg->crc == crc16_compute( p_msg->p_data, p_msg->len, 0 ) ){
					p_msg->state = PB_FSM_PACKET_RX;
					transaction_id = p_msg->id; 
				}			
			}
			else{
				p_msg->state = PB_FSM_PROGRESS; 
			}
		}
		break;
		case PB_FSM_PROGRESS: {// receive bytes 
			uint16_t bytes_remain = size;
			if( (size + p_msg->bytes_received) > p_msg->len ){
				bytes_remain =  p_msg->len - p_msg->bytes_received;
			}
			memcpy( p_msg->p_data + p_msg->bytes_received, data_in, bytes_remain );
			p_msg->bytes_received += bytes_remain;
			
			if( p_msg->bytes_received >= p_msg->len )
			{
				
				if( p_msg->crc == crc16_compute( p_msg->p_data, p_msg->len, 0 ) ){
					p_msg->state = PB_FSM_PACKET_RX; 
					// update message id 
					transaction_id = p_msg->id; 
				}
                else 
                {
                    p_msg->state = PB_FSM_IDLE;
                }
			}
		}
		break; 

		default: // do nothings 
		break; 
	}
	
	return p_msg->state; 
}

bool pb_assemble_tx_msg0( pb_msg_t * p_msg, pb_tx_data_handle_t cb)
{
	bool success = false; 
	
	uint8_t req[8];
	
	req[0] = PB_MAGIC_CODE;
	req[1] = p_msg->flag;
	
	uint16_t crc = crc16_compute( p_msg->p_data, p_msg->len, 0 );
	req[2] = (uint8_t)(p_msg->len >> 0) ;
	req[3] = (uint8_t)(p_msg->len >> 8); 
	req[4] = (uint8_t)( crc >> 0 );
	req[5] = (uint8_t)( crc >> 8 );
	req[6] = (uint8_t)( transaction_id >> 0 );
	req[7] = (uint8_t)( transaction_id >> 8 );
	
	if( cb != NULL ){
		success = cb( req, 8 );
		success &= cb( p_msg->p_data, p_msg->len ); 
	}
	
	transaction_id ++; 
	return success; 
}

bool pb_assemble_tx_msg( pb_msg_t * p_msg, pb_tx_array_handle_t cb)
{
	bool success = false; 
	
	uint8_t req[8];
	
	req[0] = PB_MAGIC_CODE;
	req[1] = p_msg->flag;
	
	uint16_t crc = crc16_compute( p_msg->p_data, p_msg->len, 0 );
	req[2] = (uint8_t)(p_msg->len >> 0) ;
	req[3] = (uint8_t)(p_msg->len >> 8); 
	req[4] = (uint8_t)( crc >> 0 );
	req[5] = (uint8_t)( crc >> 8 );
	req[6] = (uint8_t)( transaction_id >> 0 );
	req[7] = (uint8_t)( transaction_id >> 8 );
	
	if( cb != NULL ){
        uint8_array_t data_array[2] = 
        {
            {.p_data = req, .size = 8}, {.p_data = p_msg->p_data, .size = p_msg->len}
        };
		success = cb( data_array, 2 );
		//success &= cb( p_msg->p_data, p_msg->len ); 
	}
	
	transaction_id ++; 
	return success; 
}

bool pb_assemble_tx_data(uint8_t* data, uint16_t size, uint8_t flag, pb_tx_array_handle_t cb)
{
	bool success = false; 
	
	uint8_t req[8];
	
	req[0] = PB_MAGIC_CODE;
	req[1] = flag;
	
	uint16_t crc = crc16_compute( data, size, 0 );
	req[2] = (uint8_t)(size >> 0) ;
	req[3] = (uint8_t)(size >> 8); 
	req[4] = (uint8_t)( crc >> 0 );
	req[5] = (uint8_t)( crc >> 8 );
	req[6] = (uint8_t)( transaction_id >> 0 );
	req[7] = (uint8_t)( transaction_id >> 8 );
	
	if( cb != NULL ){
		uint8_array_t data_array[2] = 
        {
            {.p_data = req, .size = 8}, 
			{.p_data = data, .size = size}
        };
		success = cb(  data_array, 2 ); 
	}
	
	transaction_id ++; 
	return success; 
}

void pb_assemble_data_header(uint8_t* data, uint16_t size, uint8_t flag, pb_msg_t * p_msg)
{
	p_msg->magic = PB_MAGIC_CODE;
	p_msg->flag = flag;
	
	uint16_t crc = crc16_compute( data, size, 0 );
	p_msg->len = size ;
    p_msg->crc = crc;
    p_msg->id = transaction_id; 

    transaction_id ++; 
}

void pb_assemble_data_add_header(uint8_t* data, pb_msg_t * p_msg)
{
	data[0] = p_msg->magic;
	data[1] = p_msg->flag;
	
	data[2] = (uint8_t)( p_msg->len >> 0 ); 
	data[3] = (uint8_t)( p_msg->len >> 8 ); 
	data[4] = (uint8_t)( p_msg->crc >> 0 ); 
	data[5] = (uint8_t)( p_msg->crc >> 8 ); 
	data[6] = (uint8_t)( p_msg->id >> 0 ); 
	data[7] = (uint8_t)( p_msg->id >> 8 ); 
}

void pb_assemble_data_add_header_default(uint8_t *data, uint16_t len )
{
	data[0] = PB_MAGIC_CODE;
	data[1] = 0; 
	
	data[2] = (uint8_t)( len >> 0 );
	data[3] = (uint8_t)( len >> 8 ); 
	uint16_t crc = crc16_compute(data + 8, len, 0 );
	data[4] = (uint8_t)( crc >> 0 ); 
	data[5] = (uint8_t)( crc >> 8 ); 
	data[6] = (uint8_t)( transaction_id >> 0 ); 
	data[7] = (uint8_t)( transaction_id >> 8 ); 
	
	transaction_id ++; 
}



void pb_message_abort( pb_msg_t * msg )
{
	msg->state	= PB_FSM_IDLE;; 
	if( msg->p_data != NULL )
	{
		// free data 
	}
}


//bool pb_assemble_negative_response(pb_ret_code_t err, pb_tx_array_handle_t cb)
//{
//	uint8_t cmd[2];
//	cmd[0] = 0xff;
//	cmd[1] = err;
//	return pb_assemble_tx_data( cmd, 2, FLAG_ERROR_RESP, cb );
//}

